package drds.plus.rule_engine.rule;

import drds.plus.common.model.comparative.Comparative;
import drds.plus.rule_engine.utils.AdvancedParameterParser;
import drds.plus.rule_engine.utils.RuleUtils;
import drds.plus.rule_engine.utils.sample.Samples;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 通过描点枚举的方式实现比较树匹配
 */
public abstract class EnumerativeExpressionRule<T> extends ExpressionRule<T> {


    public EnumerativeExpressionRule(String expression) {
        super(expression);
    }

    /**
     * 解决了一个问题：若规则中同一个列出现多次，必需在列名第一次出现的地方写全参数，后续的出现可以不写
     */
    protected String parseParam(String paramInDoller, Map<String, RuleColumn> parameters) {
        RuleColumn ruleColumn = null;
        if (paramInDoller.indexOf(",") == -1) {
            // 如果没有其他参数，直接从parameters中取
            // 这里必需RuleColumn的对于Key的处理方式一致(toUpperCase())，否则将取不到，这是一个风险点
            ruleColumn = parameters.get(paramInDoller.trim().toUpperCase());
        }

        if (ruleColumn == null) {
            ruleColumn = AdvancedParameterParser.getAdvancedParamByParamTokenNew(paramInDoller, true);
            parameters.put(ruleColumn.columnName, ruleColumn);
        }
        return replace(ruleColumn);
    }

    /**
     * 允许子类根据{@linkplain RuleColumn}生成执行表达式
     *
     * @param ruleColumn
     * @return
     */
    abstract protected String replace(RuleColumn ruleColumn);

    public Map<T, Samples> calculate(Map<String, Comparative> columnNameToComparativeMap, Object outerCtx) {
        Map<String, Set<Object>> enumerates = getEnumerates(columnNameToComparativeMap);
        Map<T, Samples> res = new HashMap<T, Samples>(1);
        for (Map<String, Object> sample : new Samples(enumerates)) { // 遍历笛卡尔抽样
            T value = this.eval(sample, outerCtx);
            if (value == null) {
                throw new IllegalArgumentException("rule_engine eval resulte is null! rule_engine:" + this.expression);
            }
            Samples evalSamples = res.get(value);
            if (evalSamples == null) {
                evalSamples = new Samples(sample.keySet());
                res.put(value, evalSamples);
            }
            evalSamples.addSample(sample);
        }
        return res;
    }


    /**
     * 计算一下枚举值
     */
    private Map<String, Set<Object>> getEnumerates(Map<String, Comparative> columnNameToComparativeMap) {
        Set<RangeRuleColumn> rangeRuleColumnSet = RuleUtils.cast(this.ruleColumnSet);
        Map<String, Set<Object>> enumerates = RuleUtils.getSamplingField(rangeRuleColumnSet, columnNameToComparativeMap);
        return enumerates;
    }

}
